package org.gdstash.db;

import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
import org.gdstash.file.GDParseException;
import org.gdstash.util.GDConstants;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.sql.*;
import java.util.LinkedList;
import java.util.List;

public class DBPetSkill {
    public static final String TABLE_NAME = "GD_PET_SKILL";
    public static final String FIELD_ID = "PET_ID";
    private static final int ROW_PET_ID = 1;
    private static final int ROW_INDEX = 2;
    private static final int ROW_SKILL_ID = 3;
    private static final int ROW_FORMULA_LEVEL = 4;
    private String petID;
    private int index;
    private String skillID;
    private String formulaLevel;
    private DBSkill dbSkill;
    private Expression expressionLevel;

    public String getSkillID() {
        return this.skillID;
    }

    public void setSkillID(final String skillID) {
        this.skillID = skillID;
    }

    public int getIndex() {
        return this.index;
    }

    public void setIndex(final int index) {
        this.index = index;
    }

    public DBSkill getDBSkill() {
        return this.dbSkill;
    }

    public int getSkillLevelByPetLevel(final int level) {
        int lvl = 1;
        if (level > lvl) {
            lvl = level;
        }
        this.setCharLevel(lvl);
        return (int) (this.getSkillLevel() + 0.1);
    }

    private void setCharLevel(final int level) {
        if (this.expressionLevel == null) {
            return;
        }
        this.expressionLevel.setVariable("CHARLEVEL", (double) level);
    }

    private double getSkillLevel() {
        if (this.expressionLevel == null) {
            return 1.0;
        }
        final double value = this.expressionLevel.evaluate();
        return value;
    }

    public void setFormulaLevel(final String formulaLevel) {
        this.formulaLevel = formulaLevel;
    }

    private void setLevelFormula(final String formulaLevel) throws GDParseException {
        this.formulaLevel = formulaLevel;
        if (formulaLevel == null) {
            this.expressionLevel = null;
        } else {
            ExpressionBuilder builder = new ExpressionBuilder(formulaLevel);
            if (formulaLevel.contains("CHARLEVEL")) {
                builder = builder.variables(new String[]{"CHARLEVEL"});
            }
            try {
                this.expressionLevel = builder.build();
            } catch (Throwable ex) {
                this.expressionLevel = null;
                throw new GDParseException(ex.getMessage());
            }
        }
    }

    public static void createTable(final Connection conn) throws SQLException {
        final String dropTable = "DROP TABLE GD_PET_SKILL";
        final String createTable = "CREATE TABLE GD_PET_SKILL (PET_ID  VARCHAR(256) NOT NULL, INDEX             INTEGER, SKILL_ID          VARCHAR(256), FORMULA_LEVEL     VARCHAR(256), PRIMARY KEY (PET_ID, INDEX))";
        try (final Statement st = conn.createStatement()) {
            if (GDDBUtil.tableExists(conn, "GD_PET_SKILL")) {
                st.execute(dropTable);
            }
            st.execute(createTable);
            st.close();
            conn.commit();
        } catch (SQLException ex) {
            final Object[] args = {"GD_PET_SKILL"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
            GDMsgLogger.addError(msg);
            throw ex;
        }
    }

    public static void insert(final Connection conn, final String petID, final List<DBPetSkill> skills) throws SQLException {
        final String insert = "INSERT INTO GD_PET_SKILL VALUES (?,?,?,?)";
        if (skills == null) {
            return;
        }
        if (skills.isEmpty()) {
            return;
        }
        try (final PreparedStatement ps = conn.prepareStatement(insert)) {
            for (final DBPetSkill psk : skills) {
                if (psk.skillID == null) {
                    continue;
                }
                if (psk.formulaLevel == null) {
                    continue;
                }
                if (psk.formulaLevel.equals("0")) {
                    continue;
                }
                try {
                    final String formula = psk.formulaLevel.toUpperCase(GDConstants.LOCALE_US);
                    ExpressionBuilder builder = new ExpressionBuilder(formula);
                    if (formula.contains("CHARLEVEL")) {
                        builder = builder.variables(new String[]{"CHARLEVEL"});
                    }
                    final Expression expression = builder.build();
                    if (formula.contains("CHARLEVEL")) {
                        expression.setVariable("CHARLEVEL", 1.0);
                    }
                    expression.evaluate();
                } catch (Throwable ex) {
                    final Object[] args = {petID};
                    final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FORMULA_BY_ID", args);
                    GDMsgLogger.addWarning(msg);
                    GDMsgLogger.addWarning(ex);
                    continue;
                }
                ps.setString(1, petID);
                ps.setInt(2, psk.index);
                ps.setString(3, psk.skillID);
                ps.setString(4, psk.formulaLevel);
                ps.executeUpdate();
                ps.clearParameters();
            }
            ps.close();
        }
    }

    public static List<DBPetSkill> getByPetID(final String petID) {
        List<DBPetSkill> list = new LinkedList<DBPetSkill>();
        final String command = "SELECT * FROM GD_PET_SKILL WHERE PET_ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, petID);
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {petID, "GD_PET_SKILL"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<DBPetSkill> wrap(final ResultSet rs) throws SQLException {
        final LinkedList<DBPetSkill> list = new LinkedList<DBPetSkill>();
        while (rs.next()) {
            final DBPetSkill psk = new DBPetSkill();
            psk.petID = rs.getString(1);
            psk.index = rs.getInt(2);
            psk.skillID = rs.getString(3);
            psk.formulaLevel = rs.getString(4);
            if (psk.formulaLevel != null) {
                psk.formulaLevel = psk.formulaLevel.toUpperCase(GDConstants.LOCALE_US);
            }
            try {
                psk.setLevelFormula(psk.formulaLevel);
            } catch (GDParseException ex) {
            }
            if (psk.skillID != null) {
                psk.dbSkill = DBSkill.get(psk.skillID);
            }
            list.add(psk);
        }
        return list;
    }
}
